文章目录
  1. 1. 手写服务器总结
    1. 1.1. 搭建框架
    2. 1.2. 实现
      1. 1.2.1. 编写web.xml
      2. 1.2.2. 解析xml
        1. 1.2.2.1. dom4j解析xml
        2. 1.2.2.2. ServletContext类
        3. 1.2.2.3. WebApp类
      3. 1.2.3. servlet
        1. 1.2.3.1. 建立抽象类Servlet
        2. 1.2.3.2. 处理请求的Servlet继承Servlet
      4. 1.2.4. 封装Request对象(浏览器的请求)
      5. 1.2.5. 封装Response对象(服务器的响应)
      6. 1.2.6. 封装分发器Dispatcher
      7. 1.2.7. ServerSocket服务器
      8. 1.2.8. 解决浏览器请求的icon

手写服务器总结

搭建框架

​ …………………

实现

编写web.xml

1
2
3
4
5
6
7
8
9
10
11
12
<!--servlet处理请求-->
<servlet>
<!--LoginServlet别名:login -->
<servlet-name>login</servlet-name>
<servlet-class>com.szxy.httpserver.servlet.LoginServlet</servlet-class>
</servlet>
<!--浏览器的请求路径-->
<servlet-mapping>
<servlet-name>login</servlet-name>
<url-pattern>/login</url-pattern>
<url-pattern>/log</url-pattern>
</servlet-mapping>

​ 实体类保存解析xml中的信息

​ Entity类:servlet-name,servlet-class

​ Mapping类:servlet-name,url-pattern

解析xml

dom4j解析xml

​ Entity:servlet-name,servlet-class

​ Entity通过String name,String clazz两个成员变量保存web.xml中的\servlet-name,servlet-class

​ Mapping:servlet-name,url-pattern,

​ Mapping通过String name,和List urlPattern集合两个成员变量保存web.xml中的\servlet-name,url-pattern,

ServletContext类

​ 上下文,是一个容器,描述Entity与Mapping之间的关系

该类的成员变量

1
2
private Map<String, String> servlet; //key是servlet-name,value是servlet-class
private Map<String, String> mapping; //key是url-pattern,value是servlet-name

WebApp类

​ 1)将Entity和Mapping中的servlet-name,servlet-class,url-pattern,servlet-name

​ 保存到ServletContext类的成员变量中。

​ 请求路径作为key,获取servlet-name值,获取servlet-name

​ servlet-name作为key,获取servlet的 全路径名(包名+类名)

​ 1) 反射创建Servlet

​ 可以通过全路径名反射创建Servlet对象

1
2
3
4
5
6
7
//根据url的key获取servlet-name的值
String servletName = context.getMapping().get(url);
//根据servlet-name得到对应的servlet-class
String servletClass = context.getServlet().get(servletName); //完整的包名和类名
//使用反射创建Servlet对象
Class<?> clazz = Class.forName(servletClass);
servlet = (Servlet) clazz.newInstance();

servlet

建立抽象类Servlet

1
2
3
4
5
6
7
8
public abstract class Servlet {
public void service(Request req, Response rep)throws Exception{
this.doGet(req, rep);
this.doPost(req, rep);
}
public abstract void doGet(Request req, Response rep) throws Exception;
public abstract void doPost(Request req, Response rep) throws Exception;
}

处理请求的Servlet继承Servlet

例:登录LoginServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class LoginServlet extends Servlet{
@Override
public void doGet(Request req, Response rep) throws Exception {
// TODO Auto-generated method stub
//获取请求参数
String name = req.getParater("username");
String pwd = req.getParater("pwd");
if("bjsxt".equals(name) && "123".equals(pwd)){
//调用响应中的print方法
rep.println(name + "登录成功");
}else{
rep.println("账号或者密码不正确");
}

}

@Override
public void doPost(Request req, Response rep) throws Exception {
// TODO Auto-generated method stub

}
}

封装Request对象(浏览器的请求)

​ 通过输入流获取浏览器的请求信息,然后根据HTTP协议,进行请求信息的处理

从请求信息中获取请求方式,路径,参数,

例:GET请求和POST请求的信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
GET /log?username=bj&pwd=123&hobby=ball HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

POST /log HTTP/1.1
Host: localhost:8888
Connection: keep-alive
Content-Length: 30
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: null
Content-Type: application/x-www-form-urlencoded
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.90 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

username=bj&pwd=123&hobby=ball

请求参数的乱码问题

处理中文,因为浏览器对中文进行了编码,因此需要解码
服务器端获取到发过来的请求参数默认使用ISO8859-1进行解码操作,中文一定有乱码问题

1
2
//value是请求参数,code是编码
return URLDecoder.decode(value, code);//decode(keyValues[1].trim(), "utf-8")

封装Response对象(服务器的响应)

1.严格根据HTTP协议封装响应信息

1
2
3
4
5
6
7
8
9
//响应头
//协议,状态码,描述
headInfo.append("HTTP1.1").append(BLANK).append(code).append(BLANK).append("");
headInfo.append(CRLF);
headInfo.append("Content-Type:text/html;charset=utf-8").append(CRLF);
headInfo.append("Content-Length:"+length).append(CRLF);
headInfo.append(CRLF);
//响应正文
content.append(“<!DOCTYPE html><html><head><title>登录响应</title></head><body>登录成功</body></html>”).append(CRLF);

2.响应正文可以调用Response类中的println(String info)进行设置

​ content.append(info).append(CRLF);

封装分发器Dispatcher

​ Dispatcher是一个请求与响应,并实现了Runnable接口

​ 通过客户端连接的对象client,初始化Request和Response对象

1
2
req = new Request(client.getInputStream());
rep = new Response(client.getOutputStream());

run()方法实现

1.根据请求信息获取路径,

2.根据路径通过反射创建servlet

3.调用servlet的service()方法处理请求

4.rep.pushToClient(code);//响应,将封装后的Response信息发送给客户端

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void run() {
// TODO Auto-generated method stub
//根据不同的url创建指定的servlet对象
Servlet servlet = WebApp.getServlet(req.getUrl());
if(servlet == null){
this.code = 404; //路径找不到
}else{
//调用service方法
try {
servlet.service(req, rep);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
this.code = 500;
}
}
rep.pushToClient(code);
IOCloseUtil.closeAll(client);
}

ServerSocket服务器

​ 创建一个ServelSocket对象,监听8888端口

1
server = new ServerSocket(port);

​ 循环接收客户端请求,接收到请求,创建一个线程代理Dispatcher对象处理请求

1
2
3
4
5
6
7
8
while(!isShutDown){
//1)监听
Socket client = server.accept();
//创建线程类的对象
Dispatcher dis = new Dispatcher(client);
//创建代理,启动线程
new Thread(dis).start();
}

解决浏览器请求的icon

该请求是浏览器默认发送的:http://localhost:8888/favicon.ico

解决:

​ 1.在web.xml中配置路径

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>favicon</servlet-name>
<servlet-class>com.szxy.httpserver.servlet.FaviconServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>favicon</servlet-name>
<url-pattern>/favicon.ico</url-pattern>
</servlet-mapping>

2.创建FaviconServlet类继承Servlet

文章目录
  1. 1. 手写服务器总结
    1. 1.1. 搭建框架
    2. 1.2. 实现
      1. 1.2.1. 编写web.xml
      2. 1.2.2. 解析xml
        1. 1.2.2.1. dom4j解析xml
        2. 1.2.2.2. ServletContext类
        3. 1.2.2.3. WebApp类
      3. 1.2.3. servlet
        1. 1.2.3.1. 建立抽象类Servlet
        2. 1.2.3.2. 处理请求的Servlet继承Servlet
      4. 1.2.4. 封装Request对象(浏览器的请求)
      5. 1.2.5. 封装Response对象(服务器的响应)
      6. 1.2.6. 封装分发器Dispatcher
      7. 1.2.7. ServerSocket服务器
      8. 1.2.8. 解决浏览器请求的icon